//****************************************Copyright (c)***********************************//
//原子哥在线教学平台：www.yuanzige.com
//技术支持：www.openedv.com
//淘宝店铺：http://openedv.taobao.com 
//关注微信公众平台微信号："正点原子"，免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有，盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved
//----------------------------------------------------------------------------------------
// 文件名:           vga_driver
// 最后修改日期:      2019/7/1 9:30:00
// 最后版本:          V1.1
// 描述:            视频显示驱动模块
//----------------------------------------------------------------------------------------
// 创建者:            正点原子
// 创建日期:          2019/7/1 9:30:00
// 版本:              V1.0
// 描述:              原始版本
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module vga_driver(
    input           pixel_clk,          // 像素时钟输入
    input           sys_rst_n,          // 系统复位信号，低电平有效
    
    // RGB接口
    output          video_hs,           // 行同步信号
    output          video_vs,           // 场同步信号
    output          video_de,           // 数据使能信号
    output  [23:0]  video_rgb,          // RGB888颜色数据输出
    
    output          pixel_data_req,     // 像素数据请求信号
    input   [23:0]  pixel_data,         // 像素点数据输入
    output reg [10:0]  pixel_xpos,       // 像素点横坐标输出
    output reg [10:0]  pixel_ypos        // 像素点纵坐标输出
);

// 定义水平和垂直分辨率参数
`define Hor_Pixels 1920                  // 水平像素数
`define Ver_Pixels 1080                  // 垂直像素数

// 定义水平和垂直时序参数
`define Hor_Total_Time 2200              // 水平总时间
`define H_Right_Border 0                 // 右边界
`define H_Front_Porch 88                 // 前沿脉冲宽度
`define Hor_Sync_Time 44                 // 同步脉冲宽度
`define H_Back_Porch 148                 // 后沿脉冲宽度
`define H_Left_Border 0                  // 左边界

`define Ver_Total_Time 1125              // 垂直总时间
`define V_Bottom_Border 0                // 底边界
`define V_Front_Porch 4                  // 前沿脉冲宽度
`define Ver_Sync_Time 5                  // 同步脉冲宽度
`define V_Back_Porch 36                  // 后沿脉冲宽度
`define V_Top_Border 0                   // 上边界

// 参数定义
parameter  H_SYNC   =  `Hor_Sync_Time;   // 行同步宽度
parameter  H_BACK   =  `H_Back_Porch;  // 行后沿宽度
parameter  H_DISP   =  12'd1280;       // 行有效数据宽度
parameter  H_FRONT  =  12'd16;         // 行前沿宽度
parameter  H_TOTAL  =  `Hor_Total_Time; // 行总时间

parameter  V_SYNC   =  `Ver_Sync_Time;   // 场同步宽度
parameter  V_BACK   =  `V_Back_Porch;  // 场后沿宽度
parameter  V_DISP   =  12'd960;        // 场有效数据宽度
parameter  V_FRONT  =  12'd10;         // 场前沿宽度
parameter  V_TOTAL  =  `Ver_Total_Time; // 场总时间

// 寄存器定义
reg  [11:0] cnt_h;                      // 水平计数器
reg  [11:0] cnt_v;                      // 垂直计数器

// 有线定义
wire       video_en;                    // 视频使能信号
reg       data_req;                     // 像素数据请求寄存器

// 横向和纵向偏移量定义
reg [11:0] x_offset = 0;               // 横向偏移量
reg [11:0] y_offset = 0;               // 纵向偏移量
reg x_offset_flag;                       // 横向偏移方向标志，0为左移，1为右移
reg y_offset_flag;                       // 纵向偏移方向标志，0为上移，1为下移

// 横向偏移方向控制逻辑
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        x_offset_flag <= 1;             // 复位时设置为右移
    else if(x_offset >= `Hor_Pixels-H_DISP-2)
        x_offset_flag <= 0;             // 到达右侧边界时切换为左移
    else if(x_offset <= 1) 
        x_offset_flag <= 1;             // 到达左侧边界时切换为右移
    else x_offset_flag <= x_offset_flag;
end

// 纵向偏移方向控制逻辑
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        y_offset_flag <= 1;             // 复位时设置为下移
    else if(y_offset >= `Ver_Pixels-V_DISP-2)
        y_offset_flag <= 0;             // 到达下侧边界时切换为上移
    else if(y_offset <= 1) 
        y_offset_flag <= 1;             // 到达上侧边界时切换为下移
    else y_offset_flag <= y_offset_flag;
end

// 横向偏移量更新逻辑
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        x_offset <= 1;                  // 复位时初始化为1
    else if(cnt_h == H_TOTAL - 1'b1 && cnt_v == V_TOTAL - 1'b1)
        if(x_offset_flag)
            x_offset <= x_offset + 1;   // 右移时增加偏移量
        else   
            x_offset <= x_offset - 1;   // 左移时减少偏移量
    else x_offset <= x_offset;
end

// 纵向偏移量更新逻辑
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        y_offset <= 1;                  // 复位时初始化为1
    else if(cnt_h == H_TOTAL - 1'b1 && cnt_v == V_TOTAL - 1'b1)
        if(y_offset_flag)
            y_offset <= y_offset + 1;   // 下移时增加偏移量
        else   
            y_offset <= y_offset - 1;   // 上移时减少偏移量
    else y_offset <= y_offset;
end

// 输出信号赋值
assign video_de = (cnt_h>=`Hor_Sync_Time + `H_Back_Porch + `H_Left_Border 
            && cnt_h<=`Hor_Total_Time - `H_Front_Porch - `H_Right_Border - 1
            && cnt_v>=`Ver_Sync_Time + `V_Back_Porch + `V_Top_Border 
            && cnt_v<=`Ver_Total_Time - `V_Front_Porch - `V_Bottom_Border - 1) ? 1:0;
// 数据使能信号
assign pixel_data_req = data_req;

// 行同步信号赋值
assign video_hs = (sys_rst_n==1'b1)?((cnt_h <= `Hor_Sync_Time - 1) ? 1:0):0;

// 场同步信号赋值
assign video_vs =  (sys_rst_n==1'b1)?((cnt_v <= `Ver_Sync_Time - 1) ? 1:0):0;

// 使能RGB数据输出
assign video_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;

// RGB888数据输出
assign video_rgb = video_en ? pixel_data : 24'd0;

// 像素数据请求信号生成
always @(posedge pixel_clk) begin
    if (!sys_rst_n)begin
        data_req<='b0;
        pixel_xpos<='b0;
        pixel_ypos<='b0;
    end
    else if(((cnt_h >= H_SYNC+H_BACK-2'd2 + x_offset) && (cnt_h < H_SYNC+H_BACK+H_DISP-2'd2 + x_offset))&& ((cnt_v >= V_SYNC+V_BACK + y_offset) && (cnt_v < V_SYNC+V_BACK+V_DISP + y_offset)))begin
        data_req<='b1;
        pixel_xpos<= (cnt_h - (H_SYNC + H_BACK - 2'd2 + x_offset)); // 计算当前像素点的横坐标
        pixel_ypos<= (cnt_v - (V_SYNC + V_BACK + y_offset));       // 计算当前像素点的纵坐标
    end
    else begin
        data_req<='b0;
    end
end

// 行计数器对像素时钟计数
always @(posedge pixel_clk) begin
    if (!sys_rst_n)
        cnt_h <= 11'd0;
    else begin
        if(cnt_h < H_TOTAL - 1'b1)
            cnt_h <= cnt_h + 1'b1;
        else 
            cnt_h <= 11'd0;
    end
end

// 场计数器对行计数
always @(posedge pixel_clk) begin
    if (!sys_rst_n)
        cnt_v <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)
            cnt_v <= cnt_v + 1'b1;
        else 
            cnt_v <= 11'd0;
    end
end

endmodule